import tensorflow as tf
from sklearn.model_selection import train_test_split
from sklearn import metrics
from tensorflow import keras
from tensorflow.keras import regularizers, Input, Model
from tensorflow.keras.layers import Embedding, GlobalMaxPooling1D, Concatenate, Dropout, Dense, Lambda
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np

import os

from tensorflow.python.keras.layers import Conv1D, SimpleRNN

os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'

# 在服务上跑的数据是20201226日生成的data
# 服务器的path的内容
# path = "rawdata2020122602.csv"

path = "D:/learn/school/code/myfinalpaper/data/rawdata20201226.csv"
df = pd.read_csv(path)
x = df['data']
y = df['label']

tokenizer = keras.preprocessing.text.Tokenizer(
    filters='!"#$%&()*+,-./:;<=>?@[\\]^`{|}~\t\n')  # 创建一个Token，用来讲文本的词汇转回为索引数字,为了防止
# tokenizer = keras.preprocessing.text.Tokenizer()
tokenizer.fit_on_texts(x)
# vocab = tokenizer.word_index  # 得到每个词的编号

x_id = tokenizer.texts_to_sequences(x)

x = keras.preprocessing.sequence.pad_sequences(x_id, maxlen=400, padding='post', truncating='post')

x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=42)

y_train_class = tf.keras.utils.to_categorical(y_train, num_classes=2)

y_test_class = tf.keras.utils.to_categorical(y_test, num_classes=2)


def model_prediction(model, name, x_test, y_test):
    y_predict_list = model.predict(x_test)
    y_predict = []
    for i in y_predict_list:
        if i[0] > 0.5:
            y_predict.append(0)
        else:
            y_predict.append(1)
    y_test_truth = y_test[:, 1]
    do_metrics(y_test_truth, y_predict)
    dataframe = pd.DataFrame({'y_test_truth': y_test_truth,
                              'y_predict_score': y_predict_list[:, 1],  # 这个才是最终的内容，不要再修改其中的值了
                              'y_predict_label': y_predict})
    dataframe.to_csv("./" + name + "/" + name + '.csv', sep=',', index=False)


def do_metrics(y_test_truth, y_test_pred):
    print("metrics.accuracy_score:")
    print(metrics.accuracy_score(y_test_truth, y_test_pred))
    print("metrics.confusion_matrix:")
    print(metrics.confusion_matrix(y_test_truth, y_test_pred))
    print("metrics.precision_score:")
    print(metrics.precision_score(y_test_truth, y_test_pred))
    print("metrics.recall_score:")
    print(metrics.recall_score(y_test_truth, y_test_pred))
    print("metrics.f1_score:")
    print(metrics.f1_score(y_test_truth, y_test_pred))
    TN = metrics.confusion_matrix(y_test_truth, y_test_pred)[0, 0]
    FP = metrics.confusion_matrix(y_test_truth, y_test_pred)[0, 1]
    FN = metrics.confusion_matrix(y_test_truth, y_test_pred)[1, 0]
    TP = metrics.confusion_matrix(y_test_truth, y_test_pred)[1, 1]
    print("TN: " + str(TN))
    print("FP: " + str(FP))
    print("FN: " + str(FN))
    print("TP: " + str(TP))
    print("真正率TPR: " + str(TP/(TP+FN)))
    print("假正率FPR漏报率: " + str(FP/(FP+TN)))
    print("假负率FNR误报率: " + str(FN/(TP+FN)))
    print("真负率TNR: " + str(TN/(TN+FP)))


def do_rcnn01():
    os.mkdir("rcnn01")
    '''simpel rcnn
        only one con-layer
    '''
    print("trainning RCNN")
    print('Prepare input for model...')
    x_train_current = x_train
    # demo = np.expand_dims(x_train[:, 0], axis=1)
    # x_train[:, 0] 所有行的，第一列数据 结果为一维数组，数组长度为4664
    # np.expand_dims(x_train[:, 0], axis=1) 将一维数组转换为二维数据，(4664, 1)
    # x_train[:, 0:-1] 所有行的，截取最后一列的数据

    # 删除最后一列的内容，将所有列的内容后移一列，并复制第一列的内容放到第一列前
    x_train_left = np.hstack([np.expand_dims(x_train[:, 0], axis=1), x_train[:, 0:-1]])
    # 删除第一列的内容，将所有列的往前移一列，并复制最后一列内容放到最后一列后
    x_train_right = np.hstack([x_train[:, 1:], np.expand_dims(x_train[:, -1], axis=1)])

    x_test_current = x_test
    x_test_left = np.hstack([np.expand_dims(x_test[:, 0], axis=1), x_test[:, 0:-1]])
    x_test_right = np.hstack([x_test[:, 1:], np.expand_dims(x_test[:, -1], axis=1)])
    print('x_train_current shape:', x_train_current.shape)
    print('x_train_left shape:', x_train_left.shape)
    print('x_train_right shape:', x_train_right.shape)
    print('x_test_current shape:', x_test_current.shape)
    print('x_test_left shape:', x_test_left.shape)
    print('x_test_right shape:', x_test_right.shape)

    max_features = 204800
    maxlen = 400
    batch_size = 64
    embedding_dims = 128
    epochs = 10
    print('Build model...')
    model = RCNN(maxlen, max_features, embedding_dims)
    model.compile(loss=tf.keras.losses.CategoricalCrossentropy(from_logits=True),
                      optimizer=tf.keras.optimizers.Adam(0.001),
                      metrics=['accuracy'])
    model.fit([x_train_current, x_train_left, x_train_right],
                  y_train_class,
                  epochs=epochs,
                  validation_data=([x_test_current, x_test_left, x_test_right], y_test_class),
                  batch_size=batch_size,
                  shuffle=True
                  )
    print("*****************************************")
    print("用rcnn01模型预测")
    model_prediction(model, "rcnn01", [x_test_current, x_test_left, x_test_right], y_test_class)
    tf.saved_model.save(model, "./rcnn01/saved/1")
    print("rcnn01预测结束")
    print("*****************************************")



class RCNN(Model):
    def __init__(self,
                 maxlen,
                 max_features,
                 embedding_dims,
                 class_num=2,
                 last_activation='softmax'):
        super(RCNN, self).__init__()
        self.maxlen = maxlen
        self.max_features = max_features
        self.embedding_dims = embedding_dims
        self.class_num = class_num
        self.last_activation = last_activation
        self.embedding = Embedding(self.max_features, self.embedding_dims, input_length=self.maxlen)
        self.forward_rnn = SimpleRNN(128, return_sequences=True)
        self.backward_rnn = SimpleRNN(128, return_sequences=True, go_backwards=True)
        self.reverse = Lambda(lambda x: tf.reverse(x, axis=[1]))
        self.concatenate = Concatenate(axis=2)
        self.conv1 = Conv1D(128, kernel_size=3, activation='tanh', padding='valid', kernel_regularizer=regularizers.l2(0.001))
        # self.conv2 = Conv1D(128, kernel_size=4, padding='valid', kernel_regularizer=regularizers.l2(0.001))
        # self.conv3 = Conv1D(128, kernel_size=5, padding='valid', kernel_regularizer=regularizers.l2(0.001))
        self.concatenate_conv = Concatenate(axis=1)
        self.max_pooling = GlobalMaxPooling1D()
        self.classifier = Dense(self.class_num, activation=self.last_activation)

    def call(self, inputs):
        if len(inputs) != 3:
            raise ValueError('The length of inputs of RCNN must be 3, but now is %d' % len(inputs))
        input_current = inputs[0]
        input_left = inputs[1]
        input_right = inputs[2]
        if len(input_current.get_shape()) != 2 or len(input_left.get_shape()) != 2 or len(input_right.get_shape()) != 2:
            raise ValueError('The rank of inputs of RCNN must be (2, 2, 2), but now is (%d, %d, %d)' % (
            len(input_current.get_shape()), len(input_left.get_shape()), len(input_right.get_shape())))
        if input_current.get_shape()[1] != self.maxlen or input_left.get_shape()[1] != self.maxlen or \
                input_right.get_shape()[1] != self.maxlen:
            raise ValueError('The maxlen of inputs of RCNN must be (%d, %d, %d), but now is (%d, %d, %d)' % (
            self.maxlen, self.maxlen, self.maxlen, input_current.get_shape()[1], input_left.get_shape()[1],
            input_right.get_shape()[1]))
        embedding_current = self.embedding(input_current)
        embedding_left = self.embedding(input_left)
        embedding_right = self.embedding(input_right)
        x_left = self.forward_rnn(embedding_left)
        x_right = self.backward_rnn(embedding_right)
        x_right = self.reverse(x_right)
        x = self.concatenate([x_left, embedding_current, x_right])
        x = self.conv1(x)
        # c2 = self.conv2(x)
        # c3 = self.conv3(x)
        # x = self.concatenate_conv([c1, c2, c3])
        x = self.max_pooling(x)
        output = self.classifier(x)
        return output


if __name__ == '__main__':
    do_rcnn01()